在眾多專案中,總會有一個替公司帶進大筆營收、全年無休高速運轉,卻也讓學長姐避之唯恐不及的系統,每次有人被指派過去,附近的同事都會給一個「節哀」的眼神,然後悄悄退後一步...
主管說得都很簡單:「加一個小功能就好,三天就可以搞定了」於是我們打開專案,看到的第一個 class 就有上萬行,裡面摻著資料庫查詢、業務邏輯、寄信,還有一堆看不出來為什麼會湊在一起的東西,每次要修改都提心吊膽,像玻璃一樣碰錯一下就完蛋了,只要系統停個幾分鐘,損失的不只巨額,還有客戶對系統的信心,所以從程式碼、基礎設施到外部服務,每個環節都得要小心處理。
而這個系列會較大篇幅著重在我們最常去改、也最容易留下後患的「程式碼」開始,談談所謂的 Legacy Code。
大多數人聽到 Legacy Code,想到的通常是:
確實,我以前也是這樣理解,反正夠舊、夠醜、又不是我寫的,就叫 Legacy Code。
不過 Michael Feathers 在《Working Effectively with Legacy Code》裡提出了一個他的想法:
對我而言,Legacy Code 就是沒有測試的程式碼。
這代表昨天才推上 production 的功能,只要沒有測試保護,今天就已經是 Legacy Code 了嗎?照這個定義來看的話,是的。
這個定義跟程式碼新不新、醜不醜,甚至是不是前人寫的都沒有直接關係,重點是修改時有沒有可重複的回饋,讓我們知道原本的行為還在,很多系統沒有測試,不一定是工程師偷懶,也不一定是團隊不在乎品質,有時只是因為當時根本沒有測試觀念,或是交付壓力大到沒人停下來在乎這件事情。
剛出社會時,老闆常對我說一句話,到現在還是覺得這句話影響我很深:
當你怕麻煩,麻煩就會找上你。
還是新鮮人的時候,不知道品質對產品的影響到底會怎樣,覺得當下開發順暢舒服、速度又快、老闆也開心,沒什麼不對的,於是開開心心交付,直到兩三個月後再回頭看同一份程式碼,才發現事情已經變得複雜起來了 XD

在古希臘神話中,雅典每隔一段時間,都必須向克里特島進貢七名少年與七名少女。
這些年輕人會被送進一座錯綜複雜的迷宮,成為牛頭人身怪物「米諾陶洛斯」的祭品,於是雅典王子忒修斯自願加入這批人,打算進入迷宮殺死米諾陶洛斯,結束這場殘酷的進貢。
但真正麻煩的不只是打敗怪物,而是迷宮內部錯綜複雜,即使忒修斯成功殺死米諾陶洛斯,也很可能找不到出口,最後也會被困死在裡面。
就這麼好巧不巧,克里特島的公主阿里阿德涅愛上了忒修斯,於是交給他一團線,讓他進入迷宮時將線的一端固定在入口,沿途不斷放線。最後忒修斯成功殺死米諾陶洛斯,再循著線帶著其他雅典青年走出迷宮。
在神話中,我們得知在解決最後大魔王時,仍需要一條保命繩,當系統沒有這條繩子時,工程師自然就會變得保守:
而因為怕這些事情,最後都可能只好選擇:
加一個 if、繞過原本的設計、commit,然後開始祈禱
最後仍然被困在迷宮...
相信我,這絕對不是能力的問題,這是我們遇到 Legacy Code 時很正常的反應,而「可重複執行的測試」就是那條能帶我們回頭的線,問題在於 Legacy Code 最麻煩的地方正是我們知道需要測試,卻不知道該怎麼開始,因為要把 code 放進能執行測試的環境,往往得先調整依賴或結構,偏偏沒有測試又不敢動,但如果不動問題也不會自己消失。
補充小知識:英文 clue 原本是 clew 的另一種拼法,而 clew 早期指的是一團線,後來受到忒修斯循線走出迷宮的故事影響,才發展出「能引導人解開難題的線索」這層意思,套用在 Legacy Code 上簡直再適合不過了。
為什麼程式碼會變成這樣?緣由是什麼?前人寫這什麼爛 Code?害我現在要熬夜加班...
答案我們大概永遠不會知道,原因實在太多了,也沒有這個美國時間一個個去批判,我們能做的,是從現在開始讓系統慢慢變好,先從自己做起,如果進展順利也許還能帶動團隊一起,讓大家的日子都好過一點。
所以在這 30 天,我想分享自己在開發上學到的東西,以及維護遺留系統時累積的經驗,希望大家能帶著手上的 Legacy Code,連同每天上班的心情一起慢慢變好,這樣我們才有餘力活到老學到老嘛!
我是一名 .NET 開發者,平常喜歡研究軟體工程,也很愛讀相關書籍,其中 Michael Feathers 的經典著作《Working Effectively with Legacy Code》對我的工作幫助很大!
因此,這個系列會沿著書中的脈絡與概念往前走,整理其中對我最有價值的觀念,再加入自己的理解與實務經驗,用比較接近工作現場的方式分享。不過再次聲明,本系列講的都是我的理解,有哪裡說錯或出現偏差,請不吝批評,要噴就噴我吧,跟作者沒有關係!

圖片節錄自網路書店
我認為這本書很值得工程師買一本,供奉倒是不必,拿來看比較有用。
至於為什麼要寫這個系列,主要有三個原因。
看懂一本書和把內容寫給別人看是兩回事,閱讀時覺得自己懂了,真正開始整理才會發現,很多地方只是「看起來懂」。
參加 30 天鐵人賽,是我逼自己重新消化這些內容的方法。
我覺得對優秀的工程師來說,開發能力固然重要,「軟」實力同樣不能少,表達就是其中一項。要怎麼說人話、怎麼組織語言,真的不是一件容易的事,而我也希望透過鐵人賽來練習這方面的能力。
畢竟程式碼裡面沒有「人」,但每個段落都是「人」啊!
拜讀各位大神的文章時,我得到很大的幫助,這些文章讓我知道,原來有人已經把這些問題想得很清楚,而且願意整理出來。
如果這 30 篇文章,能讓某個正對著 Legacy Code 發呆的工程師知道:
原來不是只能硬改,還有其他方法。
如果大家能從中學到幾個走出迷宮的方法,那這個系列對我來說就有價值了。
接下來 30 天,我們可能會對自己寫過的程式碼越來越沒有信心:
這很正常,知道的越多有時反而越不敢動,但這不是壞事,真正危險的通常不是懷疑,而是毫不懷疑。
就像《駭客任務》裡的紅、藍藥丸,現在關掉文章,明天還是可以照原本的方式工作,短期內不一定會有損失,但如果願意一起走完這 30 天,應該可以學到:
面對 Legacy Code,真正需要的不是勇氣,也不是蠻力,而是一套能讓人安全前進的方法。
技術範例會以 C# 和 xUnit 為主,基本上進入實戰篇後每天都會附上程式碼,並陸續整理到 GitHub 供大家做練習,程式碼範例會分成兩個分支:
master: 最原始的 Legacy Code 範例
Refactoring: 修改重構過後的程式碼
文章會盡量用輕鬆一點的語氣來寫,如果有哪裡說得不夠精確,也請大家多多指教。
| 天數 | 內容 |
|---|---|
| Day 01 ~ 04 | 講述 Legacy Code & 單元測試 |
| Day 05 ~ 06 | 直接進入實戰,體驗一套大多數 Legacy Code 都能套用的萬用起手式 |
| Day 07 ~ 23 | 在萬用起手式的基礎上,學習不同場景可以採用的做法 |
| Day 24 ~ 29 | 延伸內容 |
| Day 30 | 賽後檢討 |
完整的 文章索引 也會陸續整理,想直接跳著讀的話請多多善用。
就醬!希望這 30 天,我們能一起把工作和生活的節奏找回來 XD